﻿using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

using alib;
using agree.configuration;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// Required property init:
	///		Isometrics
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class MrsManager : has_grammar_base, ISupportInitializeNotification,
		schema.ρρIsometrics,
		schema.ρρRelMatchHelper
	{
		public MrsManager(IRtParent parent)
			: base(parent)
		{
			ifeat_instloc = ifeat_hook = ifeat_ltop = ifeat_index = ifeat_xarg = ifeat_rels =
			ifeat_lbl = ifeat_pred = ifeat_arg0 = ifeat_carg = ifeat_body = ifeat_rstr =
			ifeat_hcons = ifeat_harg = ifeat_larg = -1;
		}

		Restrictor r_pred;
		public Restrictor PredRestrict { get { return r_pred; } }

		MrsConfig mc { get { return AgreeConfig.Mrs; } }

		public String RelationSuffix { get { return mc.RelationSuffix; } }

		RelMatchHelper rmh;
		public RelMatchHelper RelMatchHelper
		{
			get { return rmh; }
			set { throw not.valid; }
		}

		public FsPath LexKeyrelPath;
		public FsPath LexAltKeyrelPath;
		public FsPath LexPredPath;
		public FsPath LexCargPath;
		public FsPath LexAltCargPath;
		public FsPath LexAltPredPath;

		public FsPath InitialSemanticsPath;
		public FsPath CContPath;
		public FsPath LtopPath;
		public FsPath IndexPath;
		public FsPath XargPath;
		public FsPath RelsPath;
		public FsPath HconsPath;
		public FsPath FullRelsPath;
		public FsPath RelLblPath;
		public FsPath RelPredPath;

		public int ifeat_hook;
		public int ifeat_rels;
		public int ifeat_hcons;
		public int ifeat_ltop;
		public int ifeat_index;
		public int ifeat_xarg;
		public int ifeat_lbl;
		public int ifeat_pred;
		public int ifeat_arg0;
		public int ifeat_body;
		public int ifeat_rstr;
		public int ifeat_harg;
		public int ifeat_larg;
		public int ifeat_carg;
		public int ifeat_instloc;
		public int ifeat_ccont;

		public bool[] value_feat_map;
		public bool[] ignored_feat_map;
		//public byte[] pred_restrictor;
		public byte[] feat_priority_map;
		//public int[] hook_feats;

		public IComparer<int> _disp_order_cmp;

		public String LTOP { get { return ifeat_ltop == -1 ? String.Empty : ftm[ifeat_ltop].FEATURE; } }
		public String HOOK { get { return ifeat_hook == -1 ? String.Empty : ftm[ifeat_hook].FEATURE; } }
		public String INDEX { get { return ifeat_index == -1 ? String.Empty : ftm[ifeat_index].FEATURE; } }
		public String XARG { get { return ifeat_xarg == -1 ? String.Empty : ftm[ifeat_xarg].FEATURE; } }
		public String LBL { get { return ifeat_lbl == -1 ? String.Empty : ftm[ifeat_lbl].FEATURE; } }
		public String PRED { get { return ifeat_pred == -1 ? String.Empty : ftm[ifeat_pred].FEATURE; } }
		public String ARG0 { get { return ifeat_arg0 == -1 ? String.Empty : ftm[ifeat_arg0].FEATURE; } }
		public String BODY { get { return ifeat_body == -1 ? String.Empty : ftm[ifeat_body].FEATURE; } }
		public String RSTR { get { return ifeat_rstr == -1 ? String.Empty : ftm[ifeat_rstr].FEATURE; } }
		public String HARG { get { return ifeat_harg == -1 ? String.Empty : ftm[ifeat_harg].FEATURE; } }
		public String LARG { get { return ifeat_larg == -1 ? String.Empty : ftm[ifeat_larg].FEATURE; } }
		public String CARG { get { return ifeat_carg == -1 ? String.Empty : ftm[ifeat_carg].FEATURE; } }
		public String INSTLOC { get { return ifeat_instloc == -1 ? String.Empty : ftm[ifeat_instloc].FEATURE; } }

		public Type handle_type;
		public Type event_or_index_type;
#if DEPRECATED_VARIABLE_MAPPING
		public Type ref_ind_type;
		public Type non_expl_ind_type;
		public Type deg_ind_type;
		public Type event_type;
		public Type non_event_type;
#endif

		public void EndInit()
		{
			_initialize_paths();

			_initialize_misc_maps();

			_initialize_event_types();

			_initialize_display_maps();

			this._disp_order_cmp = new _mrs_feat_display_order_comparer(this);

			this.rmh = new RelMatchHelper(this);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void _initialize_paths()
		{
			FeatureInfo fi;
			var feats = ftm.feat_map;

			InitialSemanticsPath = mc.InitialSemanticsPath.Compile(ftm);

			ifeat_instloc = mc.SkolemFeat != null && feats.TryGetValue(mc.SkolemFeat, out fi) ? fi.i_feat : -1;

			path_and_last(mc.LtopPath, out LtopPath, out ifeat_ltop);

			path_and_last(mc.IndexPath, out IndexPath, out ifeat_index, mc.IndexFeat);

			path_and_last(mc.XargPath, out XargPath, out ifeat_xarg);

			path_and_first(mc.CContPath, out CContPath, out ifeat_ccont);

			if (LtopPath != null && !LtopPath.IsEmpty)
				ifeat_hook = LtopPath[0];
			else if (!IndexPath.IsEmpty)
				ifeat_hook = IndexPath[0];
			else if (!XargPath.IsEmpty)
				ifeat_hook = XargPath[0];
			else
				ifeat_hook = -1;

			path_and_first(mc.RelsPath, out RelsPath, out ifeat_rels);

			FullRelsPath = InitialSemanticsPath + RelsPath;

			path_and_last(mc.RelLblPath, out RelLblPath, out ifeat_lbl, mc.LblFeat);

			if (path_and_last(mc.RelPredPath, out RelPredPath, out ifeat_pred, mc.PredFeat))
			{
				r_pred = new Restrictor(ftm, 0, new[] { ifeat_pred });
				//pred_restrictor = new byte[ftm.Count];
				//pred_restrictor[ifeat_pred] = 1;
			}

			ifeat_arg0 = mc.Arg0Feat != null && feats.TryGetValue(mc.Arg0Feat, out fi) ? fi.i_feat : -1;
			ifeat_body = mc.BodyFeat != null && feats.TryGetValue(mc.BodyFeat, out fi) ? fi.i_feat : -1;
			ifeat_rstr = mc.RstrFeat != null && feats.TryGetValue(mc.RstrFeat, out fi) ? fi.i_feat : -1;
			ifeat_carg = mc.CargFeat != null && feats.TryGetValue(mc.CargFeat, out fi) ? fi.i_feat : -1;

			LexKeyrelPath = mc.LexKeyrelPath.Compile(ftm);
			LexAltKeyrelPath = mc.LexAltKeyrelPath.Compile(ftm);
			LexPredPath = LexKeyrelPath + ifeat_pred;
			LexCargPath = LexKeyrelPath + ifeat_carg;
			LexAltPredPath = LexAltKeyrelPath + ifeat_pred;
			LexAltCargPath = LexAltKeyrelPath + ifeat_carg;

			path_and_first(mc.HconsPath, out HconsPath, out ifeat_hcons);

			ifeat_harg = mc.HargFeat != null && feats.TryGetValue(mc.HargFeat, out fi) ? fi.i_feat : -1;
			ifeat_larg = mc.LargFeat != null && feats.TryGetValue(mc.LargFeat, out fi) ? fi.i_feat : -1;
		}

		bool path_and_first(BootstrapFsPath bp, out FsPath path_out, out int ifeat_out, String backup_feat = null)
		{
			return _p(bp, true, out path_out, out ifeat_out, backup_feat);
		}
		bool path_and_last(BootstrapFsPath bp, out FsPath path_out, out int ifeat_out, String backup_feat = null)
		{
			return _p(bp, false, out path_out, out ifeat_out, backup_feat);
		}
		bool _p(BootstrapFsPath bp, bool f_first, out FsPath path_out, out int ifeat_out, String backup_feat)
		{
			if (bp != null && !bp.IsEmpty)
			{
				path_out = bp.Compile(ftm);
				ifeat_out = f_first ? path_out.FirstIx : path_out.LastIx;
				return true;
			}
			FeatureInfo fi;
			if (backup_feat != null && ftm.feat_map.TryGetValue(backup_feat, out fi))
			{
				ifeat_out = fi.i_feat;
				path_out = new FsPath(ftm.im, new[] { ifeat_out });
				return true;
			}
			path_out = null;
			ifeat_out = -1;
			return false;
		}

		public bool IsHandleFeature(String s_feat)
		{
			return s_feat == BODY || s_feat == HARG || s_feat == LARG || s_feat == RSTR || s_feat == LBL || s_feat == LTOP;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void _initialize_misc_maps()
		{
			FeatureInfo fi;

			ignored_feat_map = new bool[ftm.Count];
			if (mc.IgnoredSemFeatures != null)
				foreach (var s in mc.IgnoredSemFeatures)
					if (ftm.feat_map.TryGetValue(s, out fi))
						ignored_feat_map[fi.i_feat] = true;
			if (mc.IgnoredExtraFeatures != null)
				foreach (var s in mc.IgnoredExtraFeatures)
					if (ftm.feat_map.TryGetValue(s, out fi))
						ignored_feat_map[fi.i_feat] = true;
			if (ifeat_instloc != -1)
				ignored_feat_map[ifeat_instloc] = true;

			value_feat_map = new bool[ftm.Count];
			if (mc.ValueFeats != null)
				foreach (var s in mc.ValueFeats)
					if (ftm.feat_map.TryGetValue(s, out fi))
						value_feat_map[fi.i_feat] = true;

			if (mc.CFrom != null && ftm.feat_map.TryGetValue(mc.CFrom, out fi))
				ignored_feat_map[fi.i_feat] = true;
			if (mc.CTo != null && ftm.feat_map.TryGetValue(mc.CTo, out fi))
				ignored_feat_map[fi.i_feat] = true;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void _initialize_display_maps()
		{
			feat_priority_map = Enumerable.Repeat<byte>(255, ftm.Count).ToArray();

			int i = 0;
			if (ifeat_pred != -1)
				feat_priority_map[ifeat_pred] = (byte)i++;

			FeatureInfo fi;
			if (mc.FeatPriorityList != null)
				foreach (var s in mc.FeatPriorityList)
					if (ftm.feat_map.TryGetValue(s, out fi))
						feat_priority_map[fi.i_feat] = (byte)i++;

			if (mc.ExtraPriorityList != null)
				foreach (var s in mc.ExtraPriorityList)
					if (ftm.feat_map.TryGetValue(s, out fi))
						feat_priority_map[fi.i_feat] = (byte)i++;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void _initialize_event_types()
		{
			if (mc.HandleType != null)
				td.TryGetType(mc.HandleType, out handle_type);
			else
				handle_type = null;

			if (mc.EventOrIndexType != null)
				td.TryGetType(mc.EventOrIndexType, out event_or_index_type);
			else
				event_or_index_type = null;

#if DEPRECATED_VARIABLE_MAPPING
			if (mc.EventType != null)
				td.TryGetType(mc.EventType, out event_type);
			else
				event_type = null;

						if (mc.NonEventType != null)
				td.TryGetType(mc.NonEventType, out non_event_type);
			else
				non_event_type = null;

			if (mc.RefIndType != null)
				td.TryGetType(mc.RefIndType, out ref_ind_type);
			else
				ref_ind_type = null;

			if (mc.NonExplIndType != null)
				td.TryGetType(mc.NonExplIndType, out non_expl_ind_type);
			else
				non_expl_ind_type = null;

			if (mc.DegIndType != null)
				td.TryGetType(mc.DegIndType, out deg_ind_type);
			else
				deg_ind_type = null;
#endif
		}

#if DEPRECATED_VARIABLE_MAPPING
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public Edge.Flag GetVariableType(Edge.Flag f)
		{
			Type tf = tu.GetEdgeType(f);
			if (event_type != null && event_type.Subsumes(tf))
				return Edge.Flag.MrsVarTypeE;
			if (ref_ind_type != null && ref_ind_type.Subsumes(tf))
				return Edge.Flag.MrsVarTypeX;
			if (non_expl_ind_type != null && non_expl_ind_type.Subsumes(tf))
				return Edge.Flag.MrsVarTypeI;
			if (deg_ind_type != null && deg_ind_type.Subsumes(tf))
				return Edge.Flag.MrsVarTypeD;
			if (handle_type != null && handle_type.Subsumes(tf))
				return Edge.Flag.MrsVarTypeH;
			if (non_event_type != null && non_event_type.Subsumes(tf))
				return Edge.Flag.MrsVarTypeP;
			if (event_or_index_type != null && event_or_index_type.Subsumes(tf))
				return Edge.Flag.MrsVarTypeI;
			return Edge.Flag.MrsVarTypeU;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public Pmrs.complex GetVariableType_new(Edge.Flag f, Pmrs.PortableMrs pmrs, String tcode)
		{
			Type tf = tu.GetEdgeType(f);
			if (event_type != null && event_type.Subsumes(tf))
				return new Pmrs.E(pmrs, tcode);
			if (ref_ind_type != null && ref_ind_type.Subsumes(tf))
				return new Pmrs.X(pmrs, tcode);
			//if (non_expl_ind_type != null && non_expl_ind_type.Subsumes(tf))
			//    return new Pmrs.I(pmrs,tcode);
			//if (deg_ind_type != null && deg_ind_type.Subsumes(tf))
			//    return new Pmrs.D(pmrs, tcode);
			if (handle_type != null && handle_type.Subsumes(tf))
				return new Pmrs.H(pmrs, tcode);
			//if (non_event_type != null && non_event_type.Subsumes(tf))
			//    return new Pmrs.P(pmrs, tcode);
			//if (event_or_index_type != null && event_or_index_type.Subsumes(tf))
			//    return new Pmrs.I(pmrs, tcode);
			//return new Pmrs.U(pmrs, tcode);
			return null;
		}
#endif


		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public bool ComparePred(TfsSlot ts_rel1, TfsSlot ts_rel2)
		{
			var x_flag = ts_rel1[ifeat_pred].EdgeFlag;
			var y_flag = ts_rel2[ifeat_pred].EdgeFlag;

			Regex rx;

			var rxd = ts_rel1.tfs.trigger_rule_regexes;

			if (rxd != null && rxd.TryGetValue(ts_rel1.ix1, out rx))
				return rx.IsMatch(tu.TypeNameOrStringValue(y_flag));

			rxd = ts_rel2.tfs.trigger_rule_regexes;

			if (rxd != null && rxd.TryGetValue(ts_rel2.ix1, out rx))
				return rx.IsMatch(tu.TypeNameOrStringValue(x_flag));

			if (x_flag == 0 || y_flag == 0 || x_flag == y_flag)
				return true;
			if ((x_flag & y_flag) >= Edge.Flag.EtmLeafType)
				return false;
			return (x_flag < y_flag ? tu.UnifyTypesHiLo(x_flag, y_flag) : tu.UnifyTypesHiLo(y_flag, x_flag)) >= 0;
		}

		public TfsSlot[] GetRelSlots(ITfsSlot ts_mrs)
		{
			TfsSlot ts;
			//return ts_mrs.Next(ifeat_rels).IsValid && RelsPath.GetSlot(ts_mrs, out ts) ? ts.ListSlots : TfsSlot.None;
			return (ts = RelsPath[ts_mrs]).IsValid ? ts.ListSlots : TfsSlot.None;
		}

		public TfsSlot[] GetQeqSlots(ITfsSlot ts_mrs)
		{
			TfsSlot ts;
			//return ts_mrs.Next(ifeat_hcons).IsValid && HconsPath.GetSlot(ts_mrs, out ts) ? ts.ListSlots : TfsSlot.None;
			return (ts = HconsPath[ts_mrs]).IsValid ? ts.ListSlots : TfsSlot.None;
		}

		public TfsSlot GetLtopSlot(ITfsSlot ts_mrs)
		{
			return LtopPath[ts_mrs];
		}

		public TfsSlot GetIndexSlot(ITfsSlot ts_mrs)
		{
			TfsSlot ts;
			return (ts = IndexPath[ts_mrs]).IsValid ? ts : TfsSlot.NotValid;
		}

		public TfsSlot GetXargSlot(ITfsSlot ts_mrs)
		{
			TfsSlot ts;
			return (ts = XargPath[ts_mrs]).IsValid ? ts : TfsSlot.NotValid;
		}

		public bool IsQuantOrWhRel(TfsSlot ts)
		{
			return ts[ifeat_rstr].IsValid && ts[ifeat_arg0].IsValid;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		sealed class _mrs_feat_display_order_comparer : IComparer<int>
		{
			public _mrs_feat_display_order_comparer(MrsManager mrsm)
			{
				this.mrs_order = mrsm.feat_priority_map;
				this.arr = mrsm.ftm.feat_arr;
			}
			readonly FeatureInfo[] arr;
			readonly byte[] mrs_order;

			public int Compare(int ifeat_1, int ifeat_2)
			{
				int d;
				if (mrs_order != null)
				{
					d = mrs_order[ifeat_1] - mrs_order[ifeat_2];
					if (d != 0)
						return d;
				}
				d = arr[ifeat_1].OrderIndex - arr[ifeat_2].OrderIndex;
				if (d != 0)
					return d;
				return ifeat_1 - ifeat_2;
			}
		};
	};
}
